package main

/*
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <net/if.h>	// struct ifreq
#include <sys/ioctl.h> 	// ioctl、SIOCGIFADDR
#include <sys/socket.h> 	// socket
#include <sys/select.h>
#include <netinet/ether.h> 	// ETH_P_ALL
#include <netpacket/packet.h> 	// struct sockaddr_ll
#include <netinet/if_ether.h>
#include <linux/sockios.h>

#define EVENT_NONE	0
#define EVENT_READ	1
#define EVENT_WRITE	2
#define EVENT_EXIT	255
#define EVENT_ERROR	-1


int rawsock_open(char* dev_name, int* ifindex, char* hwaddr)
{
	// open a socket
	int sockFd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
	if (sockFd < 0)
	{
	    perror("socket() error");
	    return sockFd;
	}

	// create a interface request structure
	struct ifreq ifr;

	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_name, dev_name, IFNAMSIZ);
	ioctl(sockFd, SIOCGIFINDEX, &ifr);
	int Socket_Index = ifr.ifr_ifindex;
	*ifindex = Socket_Index;	// for return value

	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_name, dev_name, IFNAMSIZ);
	ioctl(sockFd, SIOCGIFHWADDR, &ifr);
	memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);	// for return value

	memset(&ifr, 0, sizeof(ifr));
	strncpy(ifr.ifr_name, dev_name, IFNAMSIZ);
    ifr.ifr_flags = 0;
    ioctl(sockFd, SIOCGIFFLAGS, &ifr);
    //ifr.ifr_flags |= FLAGS.IFF_PROMISC;
    ifr.ifr_flags |= IFF_MULTICAST;
    ioctl(sockFd, SIOCSIFFLAGS, &ifr);

	// bind the socket to the interface
	struct sockaddr_ll Socket_Addr;
	memset(&Socket_Addr, 0, sizeof(Socket_Addr));
	Socket_Addr.sll_family = AF_PACKET;
	Socket_Addr.sll_protocol = htons(ETH_P_ALL);
	Socket_Addr.sll_ifindex = Socket_Index;
	bind(sockFd, (struct sockaddr *)&Socket_Addr, sizeof(Socket_Addr));

	return sockFd;
}

int rawsock_recv(int sockFd, char* buf, int buf_len)
{
	int recv_len;
	recv_len = recvfrom(sockFd, buf, buf_len, 0, NULL, NULL);
	return recv_len;
}

int rawsock_send(int sockFd, char* buf, int buf_len, int ifindex)
{
	int send_len;
	struct sockaddr_ll Socket_Addr;
	memset(&Socket_Addr, 0, sizeof(Socket_Addr));
	Socket_Addr.sll_family = AF_PACKET;
	Socket_Addr.sll_protocol = htons(ETH_P_ALL);
	Socket_Addr.sll_ifindex = ifindex;

	send_len = sendto(sockFd, buf, buf_len, 0 , (struct sockaddr *)&Socket_Addr, sizeof(Socket_Addr));
	if (send_len < 0) perror("sendto");
	return send_len;
}

int rawsock_set_multicast_filter(int sockFd, char* hwaddr, int ifindex)
{
	// add multicast addresses to the socket, based on Unit Number
	struct packet_mreq mreq;
	mreq.mr_ifindex = ifindex;
	mreq.mr_type = PACKET_MR_MULTICAST;
	mreq.mr_alen = ETH_ALEN;

	memcpy(mreq.mr_address, hwaddr, ETH_ALEN);
	setsockopt(sockFd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

	return 0;
}


int sockpair_create(int *sockpair) // int sockpair[2]: UNIX Domain socket pair: DGRAM
{
	return socketpair(PF_UNIX, SOCK_DGRAM, 0, sockpair);
}

int sockpair_event(int sockpair_1, int event)
{
	int len = write(sockpair_1, &event, sizeof(event));
	return len;
}

int rawsock_wait_event(int sockFd, int sockpair_0, double timeout)
{
	int maxfdp1, retval;
	fd_set readset;
	struct timeval to;
	int len, event;

	maxfdp1 = (sockFd > sockpair_0) ? sockFd + 1 : sockpair_0 + 1;
	FD_ZERO(&readset);
    FD_SET(sockFd, &readset); FD_SET(sockpair_0, &readset);
	to.tv_sec = (int)timeout; to.tv_usec = (timeout - to.tv_sec) * 1000000;

    retval = select(maxfdp1, &readset, NULL, NULL, &to);
    if (retval > 0) {
        if ( FD_ISSET(sockpair_0, &readset) ) {
			len = read(sockpair_0, &event, sizeof(event));
			if (len > 0) {
				return event;
			} else {
				printf("len=%d", len);
				perror("read sockpair.");
				return EVENT_ERROR;
			}
        }
		if ( FD_ISSET(sockFd, &readset) ) {
            return EVENT_READ;
        }
    } else if (retval == 0) {
        // timeout
        return EVENT_NONE;
    } else {
        // error
        return EVENT_ERROR;
    }
}

int sock_close(int fd)
{
	close(fd);
}

*/
import "C"

import (
	"errors"
	"log"
	"unsafe"
)

const (
	EVENT_NONE  int = 0
	EVENT_READ  int = 1
	EVENT_WRITE int = 2
	EVENT_EXIT  int = 255
	EVENT_ERROR int = -1
)

type Rawsock struct {
	ifname     string
	ifindex    int
	rawsock    int
	Hwaddr     [6]byte
	sockpair_0 int
	sockpair_1 int
	ChanRx     chan []byte
	ChanTx     chan []byte
}

func NewRawsock(ifname string) (*Rawsock, error) {
	var rs Rawsock
	rs.ifname = ifname

	// 创建一对socket， unix domain socket数据报，用于select等待循环中通知事件，如写报文、退出等
	var sockpair [2]int
	var c_sockpair = (*C.int)(unsafe.Pointer(&sockpair[0]))
	var c_retval = C.sockpair_create(c_sockpair)
	if int(c_retval) == 0 {
		rs.sockpair_0 = sockpair[0]
		rs.sockpair_1 = sockpair[1]
	} else {
		return nil, errors.New("create socket pair failed.")
	}
	rs.ChanRx = make(chan []byte, 256)
	rs.ChanTx = make(chan []byte, 256)

	var c_ifname = C.CString(ifname)
	defer C.free(unsafe.Pointer(c_ifname))
	var c_ifindex = C.int(0)
	var c_hwaddr = (*C.char)(unsafe.Pointer(&rs.Hwaddr[0]))
	rs.rawsock = int(C.rawsock_open(c_ifname, &c_ifindex, c_hwaddr))
	if rs.rawsock > 0 {
		rs.ifindex = int(c_ifindex)
		return &rs, nil
	} else {
		return nil, errors.New("create raw socket failed.")
	}
}

func (rs *Rawsock) Recv(b []byte) (n int, err error) {
	var c_sock = C.int(rs.rawsock)
	var c_buf = (*C.char)(unsafe.Pointer(&b[0]))
	var c_buf_len = C.int(len(b))
	var c_recv_len = C.rawsock_recv(c_sock, c_buf, c_buf_len)
	var recv_len = int(c_recv_len)
	if recv_len >= 0 {
		return recv_len, nil
	} else {
		return recv_len, errors.New("raw socket recv msg failed.")
	}
}

func (rs *Rawsock) Send(b []byte) (n int, err error) {
	var c_sock = C.int(rs.rawsock)
	var c_buf = (*C.char)(unsafe.Pointer(&b[0]))
	var c_buf_len = C.int(len(b))
	var c_ifindex = C.int(rs.ifindex)
	var c_send_len = C.rawsock_send(c_sock, c_buf, c_buf_len, c_ifindex)
	var send_len = int(c_send_len)
	if send_len >= 0 {
		return send_len, nil
	} else {
		return send_len, errors.New("raw socket send msg failed.")
	}
}

func (rs *Rawsock) SetMulticastFilter(hwaddr []byte) {
	var c_sock = C.int(rs.rawsock)
	var c_hwaddr = (*C.char)(unsafe.Pointer(&hwaddr[0]))
	var c_ifindex = C.int(rs.ifindex)
	C.rawsock_set_multicast_filter(c_sock, c_hwaddr, c_ifindex)
}

func (rs *Rawsock) WaitEvent(timeout float64) int {
	var c_sock = C.int(rs.rawsock)
	var c_sockpair_0 = C.int(rs.sockpair_0)
	var c_timeout = C.double(timeout)
	var event = int(C.rawsock_wait_event(c_sock, c_sockpair_0, c_timeout))
	return event
}

func (rs *Rawsock) EmitWriteEvent() {
	var c_sockpair = C.int(rs.sockpair_1)
	var c_event = C.int(EVENT_WRITE)
	C.sockpair_event(c_sockpair, c_event)
}

func (rs *Rawsock) EmitExitEvent() {
	var c_sockpair = C.int(rs.sockpair_1)
	var c_event = C.int(EVENT_EXIT)
	C.sockpair_event(c_sockpair, c_event)
}

func (rs *Rawsock) EventLoop() {
	var buf [2048]byte
	var recv_len int
	var recv_pkt, send_pkt []byte
	var err error

LOOP:
	for {
		e := rs.WaitEvent(0.5)
		switch e {
		case EVENT_NONE:
			continue
		case EVENT_READ: // 收到读通知， rawsock可以从网络接口读原始报文， 读取报文装入rx通道
			recv_len, err = rs.Recv(buf[0:])
			if recv_len > 0 && err == nil {
				recv_pkt = make([]byte, recv_len)
				copy(recv_pkt, buf[:recv_len])
				if len(rs.ChanRx) < cap(rs.ChanRx) {
					rs.ChanRx <- recv_pkt
				}
			} else {
				log.Println("rawsock EventLoop recv packet error.")
			}
		case EVENT_WRITE: // 自sockpair[0]收到写通知， 自tx通道读取原始报文，然后向网络接口发送
			if len(rs.ChanTx) > 0 {
				send_pkt = <-rs.ChanTx
				_, err = rs.Send(send_pkt)
				if err != nil {
					log.Println("rawsock EventLoop send packet error:", err.Error())
				}
			} else {
				log.Println("rawsock EventLoop emtpy tx queue error.")
			}
		case EVENT_EXIT: // 收到退出事件通知， 跳出主循环
			break LOOP
		case EVENT_ERROR: // select 或 read sockpair[0]出错， 一般不会发生
			log.Println("rawsock EventLoop error.")
		default: // 一般不会发生
			log.Println("rawsock EventLoop unknown event.")
		}
	}
}

func (rs *Rawsock) Close() {
	C.sock_close(C.int(rs.rawsock))
	C.sock_close(C.int(rs.sockpair_0))
	C.sock_close(C.int(rs.sockpair_1))
}
